<template>
  <div class="cl-menu-wrapper">
    <!-- 触发元素插槽 -->
    <div @click.stop="toggleMenu" @contextmenu.prevent="handleContextMenu">
      <slot name="trigger"></slot>
    </div>

    <!-- 菜单内容 -->
    <teleport to="body">
      <div
        ref="menuRef"
        class="cl-menu-container"
        v-show="isOpen"
        :style="menuStyle"
        @click.stop
      >
        <slot></slot>
      </div>
    </teleport>
  </div>
</template>

<script setup lang="ts">
import { onClickOutside } from "@vueuse/core";

const props = defineProps({
  // 菜单位置配置
  position: {
    type: [Object, String],
    default: () => ({
      x: "auto",
      y: "auto",
    }),
  },
  // 触发方式
  trigger: {
    type: String,
    default: "click", // click | contextmenu | manual
    validator: (val: string) =>
      ["click", "contextmenu", "manual"].includes(val),
  },
});

const emit = defineEmits(["open", "close"]);

const menuRef = ref<HTMLElement>();
const triggerRef = ref<HTMLElement>();
const menuStyle = ref({
  left: "0px",
  top: "0px",
  opacity: 0,
});

const isOpen = ref(false);

// 外部点击关闭
onClickOutside(menuRef, () => {
  closeMenu();
});

// 计算菜单位置
const calcPosition = (position: { x: number | string; y: number | string }) => {
  nextTick(() => {
    if (!menuRef.value) return;

    let finalX = position.x;
    let finalY = position.y;

    if (position.x === "auto" && triggerRef.value) {
      const rect = triggerRef.value.getBoundingClientRect();
      finalX = rect.left + window.scrollX;
      finalY = rect.bottom + window.scrollY + 4;
    }

    menuStyle.value = {
      left: `${finalX}px`,
      top: `${finalY}px`,
      opacity: 1,
    };
  });
};

// 切换菜单状态
const toggleMenu = () => {
  if (props.trigger === "manual") return;
  openMenu();
};

// 打开菜单
const openMenu = (position?: { x: number; y: number }) => {
  isOpen.value = true;
  // 使用新位置参数覆盖默认配置
  const newPosition = position || props.position;

  // 计算最终位置
  const finalPosition = {
    x: newPosition.x === "auto" ? "auto" : Number(newPosition.x),
    y: newPosition.y === "auto" ? "auto" : Number(newPosition.y),
  };

  // 使用新位置进行计算
  calcPosition(finalPosition);

  setupAutoClose();
};

// 关闭菜单
const closeMenu = () => {
  isOpen.value = false;
  emit("close");
};

// 处理右键事件
const handleContextMenu = (e: MouseEvent) => {
  if (props.trigger !== "contextmenu") return;

  openMenu({
    x: e.pageX,
    y: e.pageY,
  });
  e.preventDefault();
};

// 暴露方法给父组件
defineExpose({
  open: openMenu,
  close: closeMenu,
  toggle: toggleMenu,
});

// 在打开菜单时添加事件监听
const setupAutoClose = () => {
  nextTick(() => {
    const items = menuRef.value?.querySelectorAll(".menu-item");
    items?.forEach((item) => {
      item.addEventListener("click", closeMenu);
    });
  });
};

// 添加自动关闭监听
watch(isOpen, (newVal) => {
  if (newVal) {
    // 点击菜单项自动关闭
    const items = menuRef.value?.querySelectorAll(".menu-item");
    items?.forEach((item) => {
      item.addEventListener("click", closeMenu);
    });
  }
});
</script>

<style scoped>
.cl-menu-container {
  @apply absolute min-w-[120px] bg-white shadow-lg rounded-md 
         border border-gray-100 z-[9999] transition-opacity 
         duration-200 origin-top;
}

.cl-menu-wrapper {
  @apply inline-block;
}
</style>
